package com.example.quartz.util;

import com.example.common.utils.ExceptionUtil;
import com.example.common.utils.StringUtils;
import com.example.common.utils.spring.SpringUtils;
import com.example.quartz.domain.SysJob;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

/**
 * 任务执行工具
 */
public class JobInvokeUtil {
    /**
     * 执行定时任务方法
     * 根据系统任务对象中的调用目标字符串，动态解析并调用对应的方法
     *
     * @param sysJob 系统任务对象，包含任务的调用目标、执行表达式等信息
     * @throws Exception 执行过程中可能抛出的异常，包括类加载失败、方法不存在、反射调用异常等
     * @example 假设sysJob.getInvokeTarget()返回"userService.processData()"，
     * 该方法会从Spring容器中获取userService bean并调用其processData方法
     */
    public static void invokeMethod(SysJob sysJob) throws Exception {
        String invokeTarget = sysJob.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);

        if (!isValidClassName(beanName)) {
            // 不是完整的类名，尝试从Spring容器中获取bean
            Object bean = SpringUtils.getBean(beanName);
            // 调用方法(使用反射机制)
            invokeMethod(bean, methodName, methodParams);
        } else {
            // 是完整的类名，尝试直接加载类并实例化,其中有双亲委派机制,如果类加载失败,会抛出ClassNotFoundException
            Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();
            // 调用方法(使用反射机制)
            invokeMethod(bean, methodName, methodParams);
        }
    }

    /**
     * 调用任务方法（私有辅助方法）
     * 通过反射机制，在指定对象上调用指定的方法，并传入相应的参数
     *
     * @param bean         目标对象，要调用方法的对象实例
     * @param methodName   方法名称，要调用的方法名
     * @param methodParams 方法参数列表，每个元素是[参数值, 参数类型]的数组
     * @throws NoSuchMethodException     如果指定的方法不存在
     * @throws SecurityException         如果安全管理器阻止方法访问
     * @throws IllegalAccessException    如果方法无法访问
     * @throws IllegalArgumentException  如果参数不匹配
     * @throws InvocationTargetException 如果调用的方法抛出异常
     */
    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        //存在参数的时候，先获取参数类型数组和参数值数组，通过反射获取带有这些参数类型的方法，并调用该方法
        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) {
            Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        } else {
            //
            //不存在参数的时候，直接获取无参方法并调用这个方法，处理了反射调用过程中的各种异常，并将他们向上抛出
            Method method = bean.getClass().getMethod(methodName);
            method.invoke(bean);
        }
    }

    /**
     * 校验是否为有效的类名
     * 判断给定的字符串是否符合完整类名的格式（包含至少两个点号）
     *
     * @param invokeTarget 要校验的字符串
     * @return true表示是有效的类名，false表示不是
     * @example isValidClassName(" com.example.UserService ") 返回 true
     * isValidClassName("userService") 返回 false
     */
    public static boolean isValidClassName(String invokeTarget) {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }

    /**
     * 获取bean名称
     * 从调用目标字符串中提取bean名称，格式如："beanName.methodName(param1,param2)"
     * . 和 ( 之间的字符串为bean名称
     *
     * @param invokeTarget 目标字符串，格式为bean名称.方法名(参数列表)
     * @return bean名称
     * @example getBeanName(" userService.getUserById ( 1)") 返回 "userService"
     */
    public static String getBeanName(String invokeTarget) {
        //substringBefore 截取第一个出现的字符之前的字符串
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        //substringBeforeLast 截取最后一个出现的字符之前的字符串
        return StringUtils.substringBeforeLast(beanName, ".");
    }

    /**
     * 获取方法名称
     * 从调用目标字符串中提取方法名称，格式如："beanName.methodName(param1,param2)"
     * . 和 ( 之间的字符串为方法名称
     *
     * @param invokeTarget 目标字符串，格式为bean名称.方法名(参数列表)
     * @return 方法名称
     * @example getMethodName(" userService.getUserById ( 1)") 返回 "getUserById"
     */
    public static String getMethodName(String invokeTarget) {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }

    /**
     * 获取method方法参数相关列表
     * 从调用目标字符串中解析方法参数，支持String、Boolean、Long、Double和Integer类型
     * 支持的参数类型有：String、Boolean、Long、Double和Integer
     * 每个参数值后面都要跟着它的类型，类型用.class表示
     *
     * @param invokeTarget 目标字符串，格式为bean名称.方法名(参数列表)
     * @return 方法参数列表，每个元素是一个数组[参数值, 参数类型]
     * @example getMethodParams(" userService.createUser ( ' 张三 ', true, 100L, 99.5D, 20)")
     * 返回包含以下元素的列表:
     * ["张三", String.class], [true, Boolean.class], [100L, Long.class], [99.5D, Double.class], [20, Integer.class]
     */
    public static List<Object[]> getMethodParams(String invokeTarget) {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
        if (StringUtils.isEmpty(methodStr)) {
            return null;
        }
        String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++) {
            String str = StringUtils.trimToEmpty(methodParams[i]);
            // String字符串类型，以'或"开头
            if (StringUtils.startsWithAny(str, "'", "\"")) {
                classs.add(new Object[]{StringUtils.substring(str, 1, str.length() - 1), String.class});
            }
            // boolean布尔类型，等于true或者false
            else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) {
                classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
            }
            // long长整形，以L结尾
            else if (StringUtils.endsWith(str, "L")) {
                classs.add(new Object[]{Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class});
            }
            // double浮点类型，以D结尾
            else if (StringUtils.endsWith(str, "D")) {
                classs.add(new Object[]{Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class});
            }
            // 其他类型归类为整形
            else {
                classs.add(new Object[]{Integer.valueOf(str), Integer.class});
            }
        }
        return classs;
    }

    /**
     * 获取参数类型
     * 从方法参数列表中提取所有参数的类型，用于反射调用方法时指定参数类型
     *
     * @param methodParams 参数相关列表，由getMethodParams方法返回
     * @return 参数类型数组
     * @example 对于包含 ["张三", String.class], [true, Boolean.class] 的methodParams
     * 返回 [String.class, Boolean.class]
     */
    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams) {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams) {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }

    /**
     * 获取参数值
     * 从方法参数列表中提取所有参数的值，用于反射调用方法时传递参数值
     *
     * @param methodParams 参数相关列表，由getMethodParams方法返回
     * @return 参数值数组
     * @example 对于包含 ["张三", String.class], [true, Boolean.class] 的methodParams
     * 返回 ["张三", true]
     */
    public static Object[] getMethodParamsValue(List<Object[]> methodParams) {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams) {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }

}
